﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;

namespace GBricks.Collections
{
  using Properties;

  [Pure]
  public static partial class Comparers
  {
    #region IComparer<> extensions

    public static bool Equal<T>(this IComparer<T> comparer, T left, T right) {
      Argument.NotNull(comparer, "comparer");
      return comparer.Compare(left, right) == 0;
    }

    public static bool NotEqual<T>(this IComparer<T> comparer, T left, T right) {
      Argument.NotNull(comparer, "comparer");
      return comparer.Compare(left, right) != 0;
    }

    public static bool LessThan<T>(this IComparer<T> comparer, T left, T right) {
      Argument.NotNull(comparer, "comparer");
      return comparer.Compare(left, right) < 0;
    }

    public static bool LessThanOrEqual<T>(this IComparer<T> comparer, T left, T right) {
      Argument.NotNull(comparer, "comparer");
      return comparer.Compare(left, right) <= 0;
    }

    public static bool GreaterThan<T>(this IComparer<T> comparer, T left, T right) {
      Argument.NotNull(comparer, "comparer");
      return comparer.Compare(left, right) > 0;
    }

    public static bool GreaterThanOrEqual<T>(this IComparer<T> comparer, T left, T right) {
      Argument.NotNull(comparer, "comparer");
      return comparer.Compare(left, right) >= 0;
    }

    #endregion IComparer<> extensions

    public static IEqualityComparer<T> Dynamic<T>(IDictionary<Type, IEqualityComparer> cases) {
      Argument.NotNull(cases, "cases");
      Contract.Ensures(Contract.Result<IEqualityComparer<T>>() != null);

      Func<Type, IEqualityComparer> get = type => {
        IEqualityComparer comparer;
        if(!cases.TryGetValue(type, out comparer)) {
          throw ExceptionBuilder.InvalidOperation(Resources.UnexpectedComparerType1, type);
        } else {
          return comparer;
        }//if
      };

      Func<T, T, bool> equals = (left, right) => {
        if(left == null) {
          return right == null;
        } else if(right == null) {
          return false;
        }//if

        var ltype = left.GetType();
        var rtype = right.GetType();
        if(ltype != rtype) {
          return false;
        }//if

        var comparer = get(ltype);
        return comparer.Equals(left, right);
      };

      Func<T, int> hash = item => {
        if(item == null) {
          return 0;
        }//if

        var type = item.GetType();
        var comparer = get(type);
        return comparer.GetHashCode(item);
      };

      return Create(equals, hash);
    }
  }
}
